package com.lovezy.platform.filestore.service.impl;

import com.lovezy.platform.core.base.query.Pager;
import com.lovezy.platform.core.base.service.MapperService;
import com.lovezy.platform.core.cache.Cache;
import com.lovezy.platform.core.codec.CodecUtil;
import com.lovezy.platform.filestore.FileStoreCacheEnum;
import com.lovezy.platform.filestore.exception.FileStoreException;
import com.lovezy.platform.filestore.exception.FileStoreExceptionEnum;
import com.lovezy.platform.filestore.mapper.FileMapper;
import com.lovezy.platform.filestore.model.File;
import com.lovezy.platform.filestore.model.FileExample;
import com.lovezy.platform.filestore.oss.OssBucket;
import com.lovezy.platform.filestore.service.FileHelper;
import com.lovezy.platform.filestore.service.FileService;
import com.lovezy.platform.filestore.vo.FileVo;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Date;
import java.util.Map;
import java.util.Optional;

import static com.lovezy.platform.core.utils.ObjectUtil.copyTo;

/**
 * 文件服务实现
 * Created by jin on 2017/11/6.
 */

@Service
public class FileServiceImpl extends MapperService<File,Integer,FileMapper>implements FileService{

    @Autowired
    private FileHelper fileHelper;

    @Autowired
    private Cache cache;

    @Override @NotNull
    public File findFileByUrl(@NotNull String url) {
        return findFileInCache(url);
    }

    @Override @NotNull
    public File findFileById(@NotNull Integer id) {
        return findFileInCache(id);
    }

    @Override
    public ResponseEntity<byte[]> downloadFile(@NotNull String url, String downloadName) {
        File dbFile = findFileInCache(url);
        byte[] fileBytes = fileHelper.getFileByteArray(dbFile);
        String name = downloadName != null ? downloadName : dbFile.getName();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentDispositionFormData("attachment", name);
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        return new ResponseEntity<>(fileBytes, headers, HttpStatus.OK);
    }

    @Override
    public String downloadUrl(@NotNull String fileUrl) {
        if (!StringUtils.hasText(fileUrl)) {
            return null;
        }
        File file = findFileByUrl(fileUrl);
        return fileHelper.toDownloadUrl(file);
    }

    @Override
    public String downloadId(@NotNull Integer fileId) {
        File file = findFileById(fileId);
        return fileHelper.toDownloadUrl(file);
    }

    @Override
    @Transactional
    public FileVo uploadFile(MultipartFile file, String bucket, String mid) {
        String fileName = file.getOriginalFilename();
        String extension = fileHelper.getFileExtension(fileName);
        String fileUrl = CodecUtil.uuid() + extension;
        File dbFile = new File();
        dbFile.setName(fileName);
        dbFile.setContentType(file.getContentType());
        dbFile.setDir(bucket);
        dbFile.setSize(file.getSize());
        dbFile.setGmtCreate(new Date());
        dbFile.setUrl(fileUrl);
        dbFile.setCreateId(mid);
        mapper.insertSelective(dbFile);
        try {
            fileHelper.storeFile(file.getInputStream(), dbFile);
        } catch (IOException e) {
            log.error("文件存储失败", e);
            throw new FileStoreException(FileStoreExceptionEnum.FILE_STORE_ERROR);
        }
        return copyTo(dbFile, new FileVo());
    }

    @Override
    public FileVo uploadFile(java.io.File file, String mid) {
        String fileName = file.getName();
        String extension = fileHelper.getFileExtension(fileName);
        String fileUrl = CodecUtil.uuid() + extension;
        File dbFile = new File();
        dbFile.setName(fileName);
        dbFile.setContentType("");
        dbFile.setDir(OssBucket.oss.getBucket());
        dbFile.setSize(file.length());
        dbFile.setGmtCreate(new Date());
        dbFile.setUrl(fileUrl);
        dbFile.setCreateId(mid);
        mapper.insertSelective(dbFile);
        try {
            fileHelper.storeFile(new FileInputStream(file), dbFile);
        } catch (IOException e) {
            log.error("文件存储失败", e);
            throw new FileStoreException(FileStoreExceptionEnum.FILE_STORE_ERROR);
        }
        return copyTo(dbFile, new FileVo());
    }


    @NotNull
    protected File findFileInCache(@NotNull String url) {
        String cacheKey = FileStoreCacheEnum.FILE_URL.prefix() + url;
        File f = cache.get(cacheKey, () -> selectbFileByUrl(url));
        if (f == null) {
            throw new FileStoreException(FileStoreExceptionEnum.FILE_NOT_FOUND);
        }
        return f;
    }

    @NotNull
    protected File findFileInCache(@NotNull Integer id) {
        String cacheKey = FileStoreCacheEnum.FILE_ID.prefix() + id;
        File f = cache.get(cacheKey, () -> mapper.selectByPrimaryKey(id));
        if (f == null) {
            throw new FileStoreException(FileStoreExceptionEnum.FILE_NOT_FOUND);
        }
        return f;
    }

    @Nullable
    private File selectbFileByUrl(String url) {
        FileExample example = new FileExample();
        example.createCriteria()
                .andUrlEqualTo(url);
        Optional<File> fileOptional = Pager.doSingleton(() -> mapper.selectByExample(example));
        return fileOptional.orElse(null);
    }


    @Override
    public Map<String, String> createUploadToken(OssBucket bucket, String fileName) {
        return fileHelper.createToken(bucket.getBucket(), fileName);
    }

    @Override
    public File saveFile(File file) {
        if (file == null) {
            throw new FileStoreException(FileStoreExceptionEnum.FILE_STORE_ERROR);
        }
        file.setGmtCreate(new Date());
        mapper.insertSelective(file);
        return file;
    }

}
